'use client' import { useEffect, useMemo, useState } from 'react' import { useParams } from 'next/navigation' import { useApiFetch, useCreateOwneableItem, useDeleteOwnableItem, useUpdateOwnableItem, } from '@saaslib/nextjs' import { useAdminConfig } from '../../../../lib/use-admin-config' import { Badge } from '../../../../components/ui/badge' import { Button } from '../../../../components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card' import { Textarea } from '../../../../components/ui/textarea' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../../../../components/ui/table' function getNestedValue(obj: any, key: string) { return key.split('.').reduce((acc, part) => (acc ? acc[part] : undefined), obj) } function humanizeKey(value: string) { return value .replace(/([a-z])([A-Z])/g, '$1 $2') .replace(/[_-]/g, ' ') .replace(/\b\w/g, (char) => char.toUpperCase()) } export default function CollectionDetailPage() { const params = useParams() const collectionKey = typeof params?.key === 'string' ? params.key : Array.isArray(params?.key) ? params.key[0] : '' const { runtime, config } = useAdminConfig() const collection = useMemo(() => { const runtimeCollection = runtime?.collections?.find((item) => item.key === collectionKey) const configCollection = config.collections.find((item) => item.key === collectionKey) return { ...runtimeCollection, ...configCollection } }, [runtime, config, collectionKey]) const [view, setView] = useState<'mine' | 'others'>('others') const endpoint = view === 'others' ? `/${collectionKey}?admin=true` : `/${collectionKey}` const { data, loading, error, refetch } = useApiFetch<{ items: any[] }>(endpoint, { skip: !collectionKey, skipDefault: { items: [] }, }) const { createItem, loading: creating } = useCreateOwneableItem(collectionKey) const { updateItem, loading: updating } = useUpdateOwnableItem(collectionKey) const { deleteItem, loading: deleting } = useDeleteOwnableItem(collectionKey) const [selected, setSelected] = useState(null) const [createJson, setCreateJson] = useState('{}') const [updateJson, setUpdateJson] = useState('') const items = data?.items ?? [] useEffect(() => { setCreateJson(JSON.stringify(collection?.createTemplate ?? {}, null, 2)) }, [collection?.createTemplate, collectionKey]) const columns = useMemo(() => { if (collection?.fields?.length) return collection.fields const sample = items[0] if (!sample) { return [{ key: 'id', label: 'ID' }] } const preferred = ['id', '_id', 'name', 'title', 'owner', 'status', 'email', 'createdAt', 'updatedAt'] const keys: string[] = [] for (const key of preferred) { if (key in sample && !keys.includes(key)) keys.push(key) } for (const key of Object.keys(sample)) { if (keys.includes(key)) continue if (key.startsWith('_')) continue if (key.toLowerCase().includes('password')) continue keys.push(key) } return keys.slice(0, 6).map((key) => ({ key, label: humanizeKey(key) })) }, [collection, items]) const handleCreate = async () => { try { const payload = JSON.parse(createJson) await createItem(payload) refetch() } catch (err) { alert((err as Error).message) } } const handleUpdate = async () => { if (!selected) return try { const payload = JSON.parse(updateJson) const targetId = selected.id ?? selected._id if (!targetId) { throw new Error('Missing item id') } await updateItem(targetId, payload) refetch() } catch (err) { alert((err as Error).message) } } const handleDelete = async (id: string) => { if (!confirm('Delete this item? This cannot be undone.')) return await deleteItem(id) refetch() } if (!collectionKey) { return (
Collection key missing.
) } return (

{collection?.label ?? collectionKey}

View and manage owneable items from the API.

{!collection?.key && (
This collection was not found in the backend configuration.
)}
Items: {items.length}
{error && (
{error.message}
)} {columns.map((column) => ( {column.label} ))} {loading && ( Loading items… )} {!loading && items.length === 0 && ( No items found. )} {items.map((item) => ( {columns.map((column) => ( {String(getNestedValue(item, column.key) ?? '—')} ))}
))}
Create new